﻿namespace ScrumLite.Services
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Transactions;
    using Contracts;
    using System.Globalization;

    public class LinqCommitmentsService : ICommitmentsService
    {
        ScrumLite.Data.ICommitmentStore store;

        public LinqCommitmentsService()
            : this(new ScrumLite.Data.CommitmentStore())
        {
        }

        public LinqCommitmentsService(ScrumLite.Data.ICommitmentStore store)
        {
            this.store = store;
        }

        public DateTime[] GetStandupDates(string projectId)
        {
            return this.store.RetrieveStandupDates(new Guid(projectId));
        }

        public Commitment[] GetCommitments(DateTime commitmentDate, string projectId)
        {
            return Map(this.store.RetrieveCommitments(commitmentDate, new Guid(projectId)));
        }

        public Commitment GetCommitment(Guid commitmentId)
        {
            return Map(this.store.RetrieveCommitment(commitmentId));
        }

        public void Add(string projectId, Commitment commitment)
        {
            commitment.Id = Guid.NewGuid();            
            ScrumLite.Data.Commitment data = Map(commitment);
            data.initialEstimate = commitment.Left;
            data.fk_project = new Guid(projectId);

            this.store.Add(data);        
        }

        public void Update(Commitment commitment)
        {
            ScrumLite.Data.Commitment data = Map(commitment);            
            this.store.Update(data);            
        }

        public void Delete(Guid id)
        {
            using (TransactionScope scope = new TransactionScope())
            {
                this.store.Delete(id);
                scope.Complete();
            }
        }

        private static Commitment[] Map(ScrumLite.Data.Commitment[] input)
        {
            List<Commitment> output = new List<Commitment>();
            foreach (ScrumLite.Data.Commitment item in input)
            {
                output.Add(Map(item));
            }

            return output.ToArray();
        }

        internal static Commitment Map(ScrumLite.Data.Commitment input)
        {
            if (input == null)
                return null;

            Commitment output = new Commitment()
            {
                Id = input.id,
                CreatedBy = input.user_createdBy,
                CreatedOn = input.createdOn,
                Left = input.remaining.Value,
                Verification = input.verification,
                Spent = input.spent.Value,
                Title = input.title
            };

            return output;
        }

        internal static ScrumLite.Data.Commitment Map(Commitment input)
        {
            if (input == null)
                return null;

            ScrumLite.Data.Commitment output = new ScrumLite.Data.Commitment()
            {
                id = input.Id,
                user_createdBy = input.CreatedBy,
                createdOn = input.CreatedOn,
                remaining = input.Left,
                verification = input.Verification,
                spent = input.Spent,
                title = input.Title,                                
            };

            return output;
        }

        public Dictionary<int, int> GetWeeklyCommitmentsCount(string projectId, bool closed)
        {
            var query = (from Daily in this.store.RetrieveDailyCommitmentsCount(new Guid(projectId), closed)
                         group Daily by GetYearWeek(Daily.Key) into Weekly
                         select new { Week = Weekly.Key, Count = Weekly.Sum(r=>r.Value) });
            return query.ToDictionary(k => k.Week, v => v.Count);
        }

        private static int GetYearWeek(DateTime date)
        {
            CultureInfo invariant = CultureInfo.InvariantCulture;
            return invariant.Calendar.GetWeekOfYear(date,
                                   invariant.DateTimeFormat.CalendarWeekRule,
                                   invariant.DateTimeFormat.FirstDayOfWeek);
        }
    }
}
